# Virtual Router Simulator 
#
# Copyright (C) 2009-2010 VRSim Project
# Author: Choonho Son <choonho@kt.com>
# URL: <http://vrsim.googlecode.com>
# For license information, see LICENSE.TXT
#

##
import codecs

from PyQt4 import QtGui,  QtCore,  QtSvg
from xml.dom import minidom

import VRSim.Globals as globals
from VRSim.Globals.Symbols import SYMBOLS
from VRSim.Ui.Topology import Topology
from VRSim.Node.Node import Node
from VRSim.Node.GUIInfo import GUIInfo
from VRSim.Node.Juniper_M40 import Juniper_M40
from VRSim.Node.Cisco_C7200 import Cisco_C7200
from VRSim.Node.Server import Server
from VRSim.Node.Host import Host
from VRSim.Node.DHCP import DHCP
from VRSim.Node.Linux import Linux
from VRSim.Node.WindowsXP import WindowsXP
from VRSim.Link.Ethernet import Ethernet
from VRSim.Ui.Config import Config
from VRSim.Ui.QemuConfig import QemuConfig
from VRSim.Ui.DynamipsConfig import DynamipsConfig
from VRSim.Utils import debug,  message



class TopologyDesigner(QtGui.QGraphicsView):
    
    def __init__(self,  parent = None):
        QtGui.QGraphicsView.__init__(self,  parent)
        self.__topology = Topology()
        self.setScene(self.__topology)
        globals.GApp.topology = self.__topology
        
        self.setDragMode(self.RubberBandDrag)
        self.setCacheMode(self.CacheBackground)
        self.setRenderHint(QtGui.QPainter.Antialiasing)
        self.setTransformationAnchor(self.AnchorUnderMouse)
        self.setResizeAnchor(self.AnchorViewCenter)
        
        self.newedge = None
        self.resetAddingLink()
        self.reloadRenderers()
        
        self.configLink = None
        
        print "mainwindow"
        
    def reloadRenderers(self):
        self.renders = {}
        for item in SYMBOLS:
            name = item['name']
            self.renders[name] = {}
            self.renders[name]['normal'] = QtSvg.QSvgRenderer(item['normal_svg_file'])
            self.renders[name]['selected'] = QtSvg.QSvgRenderer(item['select_svg_file'])
    
    def loadXML(self,  filename):
        # recursive call xml setting process
        # TODO: encoding
        fp = codecs.open(unicode(filename).encode(globals.ENCODING),  "r",  globals.DECODING)
        if not fp:
            errmsg = "Can not open file : " + str(filename)
            message("critical", errmsg)
            return False
            
        # TODO: clear base information
        #self.clearAll(self)
        
        # Event Message
        msg = globals.eventMsg['loadXML']
        nmsg = msg % (filename)
        globals.GApp.mainWindow.NodesLog.addEvent(nmsg)
        
        # save filename
        globals.saveFilename = filename    
        fileContent = fp.read()
        # TODO (encoding check)
        node = minidom.parseString(fileContent)
        self.__topology.setXML(node)
        
        
        
    def resetAddingLink(self):
        self.__isFirstClick = True
        self.__sourceNodeID = None
        self.__destNodeID = None
        self.__sourceInterface = None
        self.__destInterface = None
        
        if self.newedge:
            self.__topology.removeItem(self.newedge)
            self.newedge = None
    
    def showContextualMenu(self):
        items = self.__topology.selectedItems()
        if len(items) == 0:
            return
        
        menu = QtGui.QMenu()
        
        instances = map(lambda item: isinstance(item, GUIInfo),  items)
        
        if True in instances:
            # Action Config
            configAct = QtGui.QAction('Configure Node',  menu)
            configAct.setIcon(QtGui.QIcon(":/symbols/config.png"))
            self.connect(configAct,  QtCore.SIGNAL('triggered()'),  self.slotConfigNode)
            
            # Action change hostname
            chHostname = QtGui.QAction('Change Hostname',  menu)
            chHostname.setIcon(QtGui.QIcon(":/symbols/config.png"))
            self.connect(chHostname,  QtCore.SIGNAL('triggered()'),  self.slotChHostname)
            
            """
            # Change from Virtual equipment to real equipment
            chEquipReal = QtGui.QAction('Change to Real Hardware',  menu)
            chEquipReal.setIcon(QtGui.QIcon(":/symbols/Change.png"))
            self.connect(chEquipReal,  QtCore.SIGNAL('triggered()'),  self.slotChEquipReal)
            
            chEquipVirtual = QtGui.QAction('Change to Virtual Machine',  menu)
            chEquipVirtual.setIcon(QtGui.QIcon(":/symbols/Change.png"))
            self.connect(chEquipVirtual,  QtCore.SIGNAL('triggered()'),  self.slotChEquipVirtual)
            """
            # Action Start
            startAct = QtGui.QAction('Start',  menu)
            startAct.setIcon(QtGui.QIcon(":/symbols/Start.png"))
            self.connect(startAct,  QtCore.SIGNAL('triggered()'),  self.slotStartNode)
            
            
            # Suspend
            suspendAct = QtGui.QAction('Suspend',  menu)
            suspendAct.setIcon(QtGui.QIcon(":/symbols/Suspend.png"))
            self.connect(suspendAct,  QtCore.SIGNAL('triggered()'),  self.slotSuspendNode)
            
            
            # Stop
            stopAct = QtGui.QAction('Stop',  menu)
            stopAct.setIcon(QtGui.QIcon(":/symbols/Stop.png"))
            self.connect(stopAct,  QtCore.SIGNAL('triggered()'),  self.slotStopNode)
            
            # delete Node
            deleteAct = QtGui.QAction('Delete Node',  menu)
            # TODO: change icon
            deleteAct.setIcon(QtGui.QIcon(":/symbols/Delete.png"))
            self.connect(deleteAct,  QtCore.SIGNAL("triggered()"),  self.slotDeleteNode)
            
            # Console
            openConsole = QtGui.QAction('Connect Console Window',  menu)
            openConsole.setIcon(QtGui.QIcon(":/symbols/Console.png"))
            self.connect(openConsole,  QtCore.SIGNAL('triggered()'),  self.slotOpenConsole)
            
            menu.addAction(configAct)
            menu.addAction(chHostname)
            # context sensitive menu
            # virtual or real
            #menu.addAction(chEquipReal)
            #menu.addAction(chEquipVirtual)
            menu.addSeparator()
            menu.addAction(startAct)
            menu.addAction(suspendAct)
            menu.addAction(stopAct)
            menu.addSeparator()
            menu.addAction(deleteAct)
            menu.addAction(openConsole)
            
            menu.exec_(QtGui.QCursor.pos())
            
            for child in menu.children():
                child.deleteLater()
     
    def showLinkMenu(self,  item): 
      
        menu = QtGui.QMenu()
        # Suspend State
        #linkAct = QtGui.QAction('Config Link',  menu)
        linkDel = QtGui.QAction('Delete Link',  menu)
        #self.connect(linkAct,  QtCore.SIGNAL('triggered()'),  self.slotConfigLink)
        self.connect(linkDel,  QtCore.SIGNAL('triggered()'),  lambda what=item: self.slotDeleteLink(item))
        
       
        #menu.addAction(linkAct)
        menu.addAction(linkDel)
        
        menu.exec_(QtGui.QCursor.pos())
        for child in menu.children():
            child.deleteLater()
            
    def addItem(self,  node):
        self.__topology.addNode(node)
     
  
    def makeNode(self,  model):
        """ return Node interface
        used for load topology
        """
        print model
        object = None
        for item in SYMBOLS:
            if item['name'] == model:
                renderer_normal = self.renders[model]['normal']
                renderer_selected = self.renders[model]['selected']
                object = item['name']
                break
                    
        if object == None:
            return
            
        node = Node(object,  renderer_normal,  renderer_selected)
        return node
    ##############################
    # Actions
    ##############################
        
    def slotShowHostname(self):
        for item in self.__topology.selectedItems():
            if not item.hostnameDisplayed():
                item.showHostname()
            else:
                item.removeHostname()
    
    def slotChHostname(self):
        for item in self.__topology.selectedItems():
            item.changeHostname()
            
    def slotChEquipReal(self):
        for item in self.__topology.selectedItems():
            nodeid = item.nodeid
            node = globals.GApp.topology.getNode(nodeid)
            node.chEquipReal()
    
    def slotChEquipVirtual(self):
        for item in self.__topology.selectedItems():
            nodeid = item.nodeid
            node = globals.GApp.topology.getNode(nodeid)
            node.chEquipVirtual()
        
    
    def __addLink(self):
        if self.__sourceNodeID == self.__destNodeID:
            self.__isFirstClick = True
            return 
        
        srcnode = globals.GApp.topology.getNode(self.__sourceNodeID)
        destnode = globals.GApp.topology.getNode(self.__destNodeID)
        
        if srcnode == None or destnode == None:
            self.__isFirstClick = True
            return
        
        if self.__topology.addLink(self.__sourceNodeID,  self.__sourceInterface,  self.__destNodeID,  self.__destInterface) == False:
            self.__isFirstClick =True
            
    def slotAddLink(self,  id,  interface):
        if id == None:
            self.__isFirstClick = True
            return
        
        if globals.addingLinkFlag:
            if self.__isFirstClick:
                self.__sourceNodeID = id
                self.__sourceInterface = interface
                self.__isFirstClick = False
                node = self.__topology.getNode(id)
                
                self.newedge = Ethernet(globals.currentLinkType,  node.guiinfo,  interface,  self.mapToScene(QtGui.QCursor.pos()),  0,  Fake = True)
                self.__topology.addItem(self.newedge)
                
            else:
                # destination node
                self.__destNodeID = id
                self.__destInterface = interface
                self.__topology.removeItem(self.newedge)
                self.newedge = None
                self.__addLink()
                self.__isFirstClick = True
    
    def slotDeleteLink(self,  edge):
        self.__topology.deleteLink(edge)
      
    def slotConfigLink(self):
        print "Link Config"
        print self.configLink
    
    # delete Node
    def slotDeleteNode(self):
        for item in self.__topology.selectedItems():
            # TODO: check Node is not working
            
            # delete link
            edges = item.getEdgeList().copy()
            for edge in edges:
                self.__topology.deleteLink(edge)
            # delete Node
            self.__topology.deleteNode(item.nodeid)
            
    def slotOpenConsole(self):
        for item in self.__topology.selectedItems():
            node = globals.GApp.topology.nodes[item.nodeid]
            node.physnode.openConsole()
        
    def scaleView(self, scale_factor):
        """ Zoom in and out
        """

        factor = self.matrix().scale(scale_factor, scale_factor).mapRect(QtCore.QRectF(0, 0, 1, 1)).width()
        if (factor < 0.20 or factor > 5):
            return
        self.scale(scale_factor, scale_factor)

    def slotConfigNode(self):
        # TODO if  update symbol
        items = self.__topology.selectedItems()
        for item in items:
            nodeid = item.nodeid
            node = globals.GApp.topology.nodes[nodeid]
            cfg = Config(node.physnode)
            cfg.show()
            cfg.exec_()
                
    
    def slotStartNode(self):
        items = self.__topology.selectedItems()
        for item in items:
            nodeid = item.nodeid
            node = globals.GApp.topology.nodes[nodeid]
            # check isVirtual
            if node.isVirtual == False:
                msg = node.physnode.hostname + " is not virtual machine"
                message(msg)
                continue
                
            # check current state
            if node.physnode.state == globals.START:
                msg = node.physnode.hostname + " is already running"
                message(msg)
                continue
                
            state = node.start()
            if state == True:
                node.physnode.state = globals.START
            else:
                node.physnode.state = globals.STOP
        # update summary
        globals.GApp.mainWindow.NodesSummary.refresh()
        
    def slotStopNode(self):
        items = self.__topology.selectedItems()
        for item in items:
            nodeid = item.nodeid
            node = globals.GApp.topology.nodes[nodeid]
            # check isVirtual
            if node.isVirtual == False:
                msg = node.physnode.hostname + " is not virtual machine"
                message(msg)
                continue
                
            # check current state
            if node.physnode.state == globals.STOP:
                msg = node.physnode.hostname + " is already stopped"
                message(msg)
                continue
            state = node.stop()
            if state == True:
                node.physnode.state = globals.STOP
            else:
                node.physnode.state = globals.START
        # update summary
        globals.GApp.mainWindow.NodesSummary.refresh()
        
    def slotSuspendNode(self):
        items = self.__topology.selectedItems()
        for item in items:
            nodeid = item.nodeid
            node = globals.GApp.topology.nodes[nodeid]
            # check isVirtual
            if node.isVirtual == False:
                msg = node.physnode.hostname + " is not virtual machine"
                message(msg)
                continue
                
            # check current state
            if node.physnode.state == globals.SUSPEND:
                msg = node.physnode.hostname + " is already suspended"
                message(msg)
                continue
            state = node.suspend()
            if state == True:
                node.physnode.state = globals.SUSPEND
            
        # update summary
        globals.GApp.mainWindow.NodesSummary.refresh()
    
###########################################
# event
###########################################
       
        
    def wheelEvent(self, event):
        """ Zoom with the mouse wheel
        """
        self.scaleView(pow(2.0, -event.delta() / 240.0))
 
    def dragMoveEvent(self, event):
        """ Drag move event
        """

        event.accept()                
            
    def dropEvent(self,  event):
        print "Dropevent"
        if event.mimeData().hasText():
            symbolname = str(event.mimeData().text())
            print symbolname
            object = None
            
            for item in SYMBOLS:
                if item['name'] == symbolname:
                    renderer_normal = self.renders[symbolname]['normal']
                    renderer_selected = self.renders[symbolname]['selected']
                    object = item['name']
                    break
                    
            if object == None:
                return
            print object
            node = Node(object,  renderer_normal,  renderer_selected)
            # add node
            self.__topology.addNode(node)
            node.guiinfo.setPos(self.mapToScene(event.pos()))
            # update domain information
            node.updateVM()
            
        #    if globals.GApp.workspace.flg_showHostname = True:
        #        node.guiinfo.showHostname()
                
            
            # Center the node
            pos_x = node.guiinfo.pos().x() - (node.guiinfo.boundingRect().width() / 2)
            pos_y = node.guiinfo.pos().y() - (node.guiinfo.boundingRect().height() / 2)
            print "Node Position : ",  pos_x,  pos_y
            node.physnode.setPos(pos_x,  pos_y)
            node.guiinfo.setPos(pos_x,  pos_y)
            
            event.setDropAction(QtCore.Qt.MoveAction)
            event.accept()
        
        else:
            event.ignore()
    
    def mousePressEvent(self,  event):
        show = True
        item = self.itemAt(event.pos())
        
        #print type(item)
        # link menu
        if event.button() == QtCore.Qt.RightButton and type(item) == Ethernet:
            print "### Link Menu Enter"
            self.showLinkMenu(item)
            show = False
        
        # node menu
        if show and event.button() == QtCore.Qt.RightButton and not globals.addingLinkFlag:
            #QtGui.QGraphicsView.mousePressEvent(self, event)
            if item:
                # deselect previous selected item
                items = self.__topology.selectedItems()
                for _item in items:
                    _item.setSelected(False)
                # select current item
                item.setSelected(True)
            self.showContextualMenu()
        else:
            QtGui.QGraphicsView.mousePressEvent(self, event)
        

    def mouseMoveEvent(self,  event):
        if (self.newedge):
            self.newedge.setMousePoint(self.mapToScene(event.pos()))
            event.ignore()
        else:
            QtGui.QGraphicsView.mouseMoveEvent(self, event)
